/*
The surface-physics project: http:// code.google.com/p/surface-physics/
Adds real world physics based on the Bullet physics engine, to bases in the Orbiter space
flight simulation software.

Written by Abhijit Nandy
License : Apache License 2.0

Bump_BulletBase.h
BulletBase management directly from within Bump. Objects of this class are created
in the Bump module and uses a BulletBase VESSEL as a proxy for inserting meshes and attaching
objects. The BulletBase has no other role other than key forwarding (as of now) which is
difficult to do from a module. This class merges Orbiter and Bullet functionality towards
achieving the above outlined purpose.

This is possible due to the extensive public API of VESSEL & its derived classes.

This file declares the interface for the Bump_BulletBase class. Its role:
1. Creates the BulletBase proxy VESSEL at the location of the passed base
2. Reads the base specific physics file and staggers creation of objects in update()
   which is called at every sim step.
3. Reads command from a BulletBase public member and passes it on to components/attachments or
    processes them itself as reqd.
4. Scans the sky for nearby objects and attaches/detaches them as needed(does not scan in every step)


Do not store any global positions, or any positions, always get them again when needed(they all change)
OBJHANDLEs can be stored like handle to base or reference body. These wont change.


*/


#pragma once

// Orbiter
#include "orbitersdk.h"
#include "BulletBase.h"
#include "Bump_VesselCollisionDefCache.h"

// STL
#include <list>
#include <unordered_map>

// TODO : Bullet - remove and only include specific stuff
#include "btBulletDynamicsCommon.h"

#define MAX_OBJECTS 10
#define MAX_STRLEN 50

// Useful macros
#define V3ARGS(a)    (a).x, (a).y, (a).z
#define V3ARGSF(a)   (btScalar)(a).x, (btScalar)(a).y, (btScalar)(a).z
#define BT3ARGS(a)   (a).x(), (a).y(), (a).z()
#define BTZEROVECTOR btVector3(0., 0., 0.)

class Bump_BaseObject;
class Bump_TerrainPatch;
enum TerrainType;
enum VisualizerCameraMode;

extern bool singleStep;

/**
 * Based on last action, a FSM based on Orbiter's time step
 * Used for gradual loading of objects into the simulation
 */
static enum BaseMode
{
    BM_ERROR,
    BM_CREATE_BB,
    BM_CHECK_TERRAIN,
    BM_RESTORE_STATE,
    BM_CHECK_FILE,    // loading of objects in this state till end of physics file
    BM_PARSE_FILE,
    BM_RUNNING    // Normal running mode after all objects loaded
};

class Bump_BulletBase;

struct InitialObjectData {
	char *strType;
	char *strObjName;
	OBJHANDLE hObj;
	Bump_BulletBase *base;
	VesselCollisionDef* pVesselCollDef;
};


// This map stores a list of struct BaseObject*, NOT Bump_BaseObject
typedef std::list<Bump_BaseObject*> BaseObjectList;

typedef std::tr1::unordered_map<OBJHANDLE, Bump_BaseObject*> BaseObjectMap;// Orbiter-Bump object map
typedef std::tr1::unordered_map<std::string, Bump_BaseObject*> BaseObjectNameMap;// Orbiter-Bump object map


/**
 * The Bump_BulletBase to create and manage a BulletBase
 */
class Bump_BulletBase
{
// #pragma message("Bump_BulletBase.h included")

	

    // Section cutoff point : should there be section specific cutoffs read from file ?
    double cutoff;

    // used while ranging : do not delete !!
    // FIXME: The max elevation is different for different planets , do not make this a const
    static const double MAX_TERRAIN_ELEVATION;

    // How much time to wait for Orulex initialize
    // FIXME: This should be read from a file
    static const double MAX_TERRAIN_GEN_INIT_TIME;
    static const double MAX_TERRAIN_GEN_SWITCH_TIME;


    // Bullet's representation of the ground static rigid body - used only for testing
    btCollisionShape    *groundShape, *supportPlaneShape;
    btRigidBody    *groundRigidBody;

    // The primary rigid body of this base, used for focusing on the base, this should be a tower
    // or pad at the base's origin. Currently its a box at the base's origin.
    btRigidBody *primaryRigidBody;
    btCollisionShape *primaryBodyCollisionShape;



    // The vessel collision def cache
    Bump_VesselCollisionDefCache mCollDefCache;

    // Physics file reading utilities
    void    readPhysicsFileDescription(void);
    void    readPhysicsFileObject(void);

    // Objects factory based object creation
    int     createBaseObject(InitialObjectData& initData);

    // Create the appropriate ground for this base at proper offset
    // An entire thick plane is allocated to the base
    int     createGround(TerrainType terrainType);
    int     createGroundAtPos(TerrainType terrainType, btVector3& pos);

    // Create the base vessel
    int     createBaseVessel(void);

    // Switch-case based object creation
    int     createBaseObject(char *strType, char *strObjName, char *line);

    // Ranging pass to get distances of vessels from base
    void    rangingPass(bool skipLanded);

    bool    checkBaseVesselExists(void);
    bool    checkAttachments(VESSEL3 *v);

    int     printControlMap(void);
    int     printObjectList(void);


public:

    // Constants
    static const int BULLET_WORLD_SIZE = 10000;

    // Base Name
    char strBaseName[MAX_STRLEN];

    // Latitude, longitude, radius of proxy base vessel: may not match ref body if base above surface
    double lng, lat, rad;

    // Mass of this base
    double mass;

    // Offset of this base in physics world
    btVector3 simBoxOffset;

    // Handle to Orbiter base if one exists(not the BulletBase), e.g. Moon for Brighton Beach.
    OBJHANDLE hObjBase;

    // Handle to reference body OBJECT
    OBJHANDLE hObjRefBody;

    // Radius if reference body (rad >= radRefBody)
    double radRefBody;

    // Gravitational force of reference body
    VECTOR3 G;

    // Pointers to associated BulletBase, to read commands from it
    OBJHANDLE hObjBulletBase;
    VESSEL3 *vBulletBase;
    BulletBase *bb;

    // Used to track loads of objects
    BaseMode mode;

    // Objects managed by this base
    BaseObjectList baseObjList;
    BaseObjectList::iterator objIter;

    // Unordered map using hashes for keeping controlled VESSELs to skip against
    BaseObjectMap objMap;
    BaseObjectMap::iterator objMapIter;

    // Unordered map with the name of the object as key, used to prevent duplication
    // of objects already created during scenario load
    BaseObjectNameMap objNameMap;


    // Physics file : used to read object configurations
    FILEHANDLE physicsFile;

    // Physics stuff, dynamics world maintained by Bump ? Or base ?
    btDynamicsWorld *m_dynamicsWorld;

    // General Physics world variables
    btAlignedObjectArray<btCollisionShape*> *m_collisionShapes;

    // True if this base uses a terrain generator
    TerrainType terrainGenerator;
    double maxTerrainRadialDist, maxTerrainElevation;

    // These are same across all bases
	static bool simIsEnding;
	static double mTimeWarpFactor;

	// Per base freeze flag
	bool mFrozen;


    Bump_BulletBase();
    virtual ~Bump_BulletBase();

    // Main base management functions
    int     clbkSetClassCaps(OBJHANDLE hObj, OBJHANDLE hObjRBody);
    int     clbkPreStep(double SimT, double SimDT, double MJD);
    int     clbkPostStep(double SimT, double SimDT, double MJD);


    // Section update
    void    refreshSections(void);

    // Gettors
    BaseObjectList& getObjects();


    // Forced drop of objects by 3rd party(1st party is base, 2nd is baseobject, 3rd is some external object)
    int     dropBaseObject(OBJHANDLE hObjDrop);


    void     setDynamicsWorld(btDynamicsWorld* dynamicsWorld)
    { m_dynamicsWorld = dynamicsWorld; }

    void     setCollisionShapesArray(btAlignedObjectArray<btCollisionShape*> *collisionShapes)
    { m_collisionShapes = collisionShapes; }

    // Multiple bases
    void     setPhysicsWorldOffset(btVector3& physicsWorldOffset)
    {
        simBoxOffset = physicsWorldOffset;
        oapiWriteLogV("simBoxOffset: %f, %f, %f", simBoxOffset.x(), simBoxOffset.y(), simBoxOffset.z());
    }

    // Required for the Orbiter synced camera mode : F10 and  the GUI synced camera mode : F11
    void    setCameraFocus(VisualizerCameraMode mode);

};





